package lk.icta.common.util;

import javax.activation.MimetypesFileTypeMap;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Calendar;

import lk.icta.common.AlfrescoConstant;
import lk.icta.common.BusinessException;
import lk.icta.common.entity.Document;
import lk.icta.core.entity.Land;
import org.alfresco.webservice.authentication.AuthenticationResult;
import org.alfresco.webservice.authentication.AuthenticationServiceLocator;
import org.alfresco.webservice.authentication.AuthenticationServiceSoapBindingStub;
import org.alfresco.webservice.content.Content;
import org.alfresco.webservice.content.ContentServiceSoapBindingStub;
import org.alfresco.webservice.repository.QueryResult;
import org.alfresco.webservice.repository.UpdateResult;
import org.alfresco.webservice.types.CML;
import org.alfresco.webservice.types.CMLCreate;
import org.alfresco.webservice.types.CMLDelete;
import org.alfresco.webservice.types.CMLUpdate;
import org.alfresco.webservice.types.ContentFormat;
import org.alfresco.webservice.types.NamedValue;
import org.alfresco.webservice.types.ParentReference;
import org.alfresco.webservice.types.Predicate;
import org.alfresco.webservice.types.Query;
import org.alfresco.webservice.types.Reference;
import org.alfresco.webservice.types.ResultSet;
import org.alfresco.webservice.types.ResultSetRow;
import org.alfresco.webservice.util.AuthenticationUtils;
import org.alfresco.webservice.util.Constants;
import org.alfresco.webservice.util.ContentUtils;
import org.alfresco.webservice.util.Utils;
import org.alfresco.webservice.util.WebServiceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class AlfrescoUtil.
 */
public class AlfrescoUtil {

    /**
     * The Constant LOGGER.
     */
    private static final Logger logger = LoggerFactory.getLogger(AlfrescoUtil.class);

    /**
     * Adds the content.
     *
     * @param document the document
     */
    public static void addContent(Document document) throws Exception {

        // String fileName = document.getId() + document.getExtension().toLowerCase();
        String fileName = document.getId() + "";

        logger.debug("Starting alfresco put for the document: " + document);
        //String fileName = document.getDocumentName();
        logger.debug("FileName is : " + fileName);

        try {
            //WebServiceFactory.setEndpointAddress(AlfrescoConstant.SERVER_URL + AlfrescoConstant.API);
            WebServiceFactory.setEndpointAddress(AlfrescoConstant.SERVER_URL);
            //AuthenticationUtils.startSession(AlfrescoConstant.USER_NAME, AlfrescoConstant.PASSWORD);
            AuthenticationUtils.startSession("admin", "admin");

            // String mimeType = getMimeType(fileName);
            String mimeType = document.getContentType();
            logger.debug("Mimetype is : " + mimeType);
            logger.debug("Upload URL path : " + document.getUploadUrl());

            String[] spaceNameArray = document.getUploadUrl().split("/");
            logger.debug("spaces to be created: "
                    + Arrays.toString(spaceNameArray));

            boolean isFirstSapce = true;
            Reference parentReference = null;

            for (String spaceName : spaceNameArray) {
                logger.debug("spaces to be created : " + spaceName);
                parentReference = isFirstSapce ? getCompanyHome()
                        : parentReference;
                isFirstSapce = false;
                parentReference = createSpace(parentReference, spaceName);
            }
            String path = convertToLucenePath(document.getUploadUrl());
            ResultSetRow row = findItem(path, fileName);
            if (row != null) {
                String fileNameNew = fileName;
                while (true) {
                    fileNameNew = getAlternativeFileName(fileNameNew);
                    ResultSetRow row1 = findItem(path, fileNameNew);
                    if (row1 == null) {
                        // Create a reference to the parent where we want to
                        // create content
                        ParentReference parent = new ParentReference(
                                AlfrescoConstant.STORE, null, path,
                                Constants.ASSOC_CONTAINS, null);

                        // Assign name
                        parent.setChildName(AlfrescoConstant.ALFRESCO_METADATA_PREFIX
                                + fileNameNew);

                        NamedValue[] contentProps = new NamedValue[2];
                        contentProps[0] = Utils.createNamedValue(
                                Constants.PROP_NAME, fileNameNew);
                        contentProps[1] = Utils.createNamedValue(
                                Constants.PROP_TITLE, fileNameNew);

                        String uuid = row.getNode().getId();
                        // get a reference to the content we want to delete
                        Reference reference = new Reference(
                                AlfrescoConstant.STORE, uuid, null);
                        Predicate wherePredicate = new Predicate(
                                new Reference[]{reference}, null, null);
                        CML cml = new CML();
                        CMLUpdate update = new CMLUpdate(contentProps,
                                wherePredicate, null);

                        cml.setUpdate(new CMLUpdate[]{update});
                        WebServiceFactory.getRepositoryService().update(cml);
                        break;
                    }
                }
            }

            // Create a reference to the parent where we want to create content
            ParentReference parent = new ParentReference(
                    AlfrescoConstant.STORE, null, path,
                    Constants.ASSOC_CONTAINS, null);

            // Assign name
            parent.setChildName(AlfrescoConstant.ALFRESCO_METADATA_PREFIX
                    + fileName);

            NamedValue[] contentProps = new NamedValue[2];
            contentProps[0] = Utils.createNamedValue(Constants.PROP_NAME,
                    fileName);
            contentProps[1] = Utils.createNamedValue(Constants.PROP_TITLE,
                    fileName);
            CMLCreate create = new CMLCreate(AlfrescoConstant.DUMMY_ID, parent,
                    null, null, null, Constants.TYPE_CONTENT, contentProps);

            // Construct CML Block
            CML cml = new CML();
            cml.setCreate(new CMLCreate[]{create});

            UpdateResult[] result = WebServiceFactory.getRepositoryService()
                    .update(cml);
            Reference content = result[0].getDestination();
            ContentServiceSoapBindingStub contentService = WebServiceFactory
                    .getContentService();
            ContentFormat contentFormat = new ContentFormat(mimeType,
                    AlfrescoConstant.UTF_8);
            Content c = contentService.write(content, Constants.PROP_CONTENT,
                    document.getFileContent(), contentFormat);
            // Set the UUID as document ID
            document.setDocumentUuid(c.getNode().getUuid());

            if (logger.isDebugEnabled()) {
                logger.debug("File named " + fileName
                        + " added successfully having UUID: "
                        + document.getDocumentUuid());
            }

            // let the byte array be garbage collected
            document.setFileContent(null);

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("Add operation failed", e);
            // throw new BusinessException("tender.docupload.failed");
        } finally {
            AuthenticationUtils.endSession();
        }
    }

    /**
     * Delete content.
     *
     * @param document the document
     * @throws BusinessException the business exception
     */
    /* public static void deleteContent(DocumentVO document)
      throws BusinessException {
      logger.debug("START delete ExistingFile: " + document.getDocumentName());

      try {
          WebServiceFactory.setEndpointAddress(AlfrescoConstant.SERVER_URL
              + AlfrescoConstant.API);
          AuthenticationUtils.startSession(AlfrescoConstant.USER_NAME,
              AlfrescoConstant.PASSWORD);

          String path = convertToLucenePath(document.getUploadUrl());
          ResultSetRow row = findItem(path, document.getDocumentName());
          if (row != null) {
              String uuid = row.getNode().getId();
              // get a reference to the content we want to delete
              Reference reference = new Reference(AlfrescoConstant.STORE,
                  uuid, null);
              Predicate wherePredicate = new Predicate(
                  new Reference[]{reference}, null, null);
              CML cml = new CML();
              CMLDelete delete = new CMLDelete(wherePredicate);
              cml.setDelete(new CMLDelete[]{delete});
              WebServiceFactory.getRepositoryService().update(cml);
          }

      } catch (Exception e) {
          logger.error("Delete operation failed", e);
          throw new BusinessException("tender.doc.delete.failed");
      } finally {
          AuthenticationUtils.endSession();
      }

      if (logger.isDebugEnabled()) {
          logger.debug("END delete Existing File: "
              + document.getDocumentName());
      }

  }  */

    /**
     * Download file.
     *
     * @param document the document
     * @return the byte[]
     * @throws BusinessException the business exception
     */
    public static byte[] downloadFile(Document document)
            throws BusinessException {

        if (logger.isDebugEnabled()) {
            logger.debug("STARTING... download File: "
                    + document.getDocumentName());
        }

        byte[] content = null;

        try {
            WebServiceFactory.setEndpointAddress(AlfrescoConstant.SERVER_URL);
            //    + AlfrescoConstant.API);
            AuthenticationUtils.startSession(AlfrescoConstant.USER_NAME,
                    AlfrescoConstant.PASSWORD);
            String filePath = convertToLucenePath(document.getUploadUrl() + "/"
                    + document.getId());
            logger.debug("File path: " + filePath);
            Reference fileReference = new Reference(AlfrescoConstant.STORE,
                    document.getDocumentUuid(), filePath);
            content = getContent(fileReference);

        } catch (Exception e) {
            logger.error("Download operation failed", e);
            throw new BusinessException("tender.doc.download.failed");
        } finally {
            AuthenticationUtils.endSession();
        }

        if (logger.isDebugEnabled()) {
            logger.debug("END of download File: " + document.getDocumentName());
        }

        return content;
    }

    /**
     * Gets the alternative file name.
     *
     * @param fileName the file name
     * @return the alternative file name
     */
    private static String getAlternativeFileName(String fileName) {
        String alterFileName = null;

        // Check if file name ends with a digit or not
        if (Character.isDigit(fileName.charAt(fileName.length() - 1))) {
            // Get the numeric value of last character
            int i = Character
                    .getNumericValue(fileName.charAt(fileName.length() - 1));

            alterFileName = fileName.substring(0, fileName.length() - 1)
                    + (i + 1);
        } else {
            alterFileName = fileName + 1;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("File name to be created: " + alterFileName);
        }
        return alterFileName;
    }

    /**
     * Generic method can be used for other VOs also
     * <p/>
     * Get URL from the object.
     *
     * @param object the object
     * @return the url for alfresco server
     * @throws Exception the exception
     */
    /*public static String getUrlForAlfrescoServer(Object object)
        throws Exception {

        String url = "";

        if (object instanceof TenderVO) {
            url = getURLForTenderVO((TenderVO) object);
        }

        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Got URL is: " + url);
        }

        return url;
    }  */
    public static String getUrlForAlfrescoServer(long landId)
            throws Exception {

        String url = "";

        url = getURLForLand(landId);
        /*
        if (object instanceof Land) {
            //url = getURLForLand((Land) object);
            url = getURLForLand(landId);
        }  */

        if (logger.isInfoEnabled()) {
            logger.info("Got URL is: " + url);
        }

        return url;
    }

    /**
     * Gets the content.
     *
     * @param node the node
     * @return the content
     * @throws Exception the exception
     */
    private static byte[] getContent(Reference node) throws Exception {
        Content content = null;

        if (logger.isDebugEnabled()) {
            logger.debug("Getting content of document with path "
                    + node.getPath() + " or id " + node.getUuid() + ".");
        }

        try {
            Content[] read = WebServiceFactory.getContentService().read(
                    new Predicate(new Reference[]{node},
                            AlfrescoConstant.STORE, null),
                    Constants.PROP_CONTENT);
            content = read[0];
            if (logger.isDebugEnabled()) {
                logger.debug("Got " + read.length + " content elements.");
                logger.debug("The first content element has a size of "
                        + content.getLength() + " segments.");
            }
        } catch (Exception e) {
            logger.error("Can not get the content.", e);
            throw new BusinessException("tender.doc.download.failed");
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        InputStream in = ContentUtils.getContentAsInputStream(content);
        byte[] buf = new byte[2048];
        int size;
        while ((size = in.read(buf)) != -1) {
            out.write(buf, 0, size);
        }

        return out.toByteArray();
    }

    /**
     * Gets the uRL for tender vo.
     *
     * @param tender the tender
     * @return the uRL for tender vo
     * @throws Exception the exception
     */
    /* private static String getURLForTenderVO(TenderVO tender) throws Exception {
      String rootFolderInAlfresco = AlfrescoCrendentialInfo.getRootUrl();

      String year = AlfrescoConstant.PREFIX_YEAR + getCurrentFiscalYear();
      String month = getCurrentMonth();
      String tenderId = AlfrescoConstant.PREFIX_TENDER + tender.getId();

      return new StringBuilder(rootFolderInAlfresco).append("/")
          .append(tender.getDepartmentName()).append("/").append(year)
          .append("/").append(month).append("/").append(tenderId)
          .toString();
  }  */
    private static String getURLForLand(long landId) throws Exception {
        // String rootFolderInAlfresco = AlfrescoCrendentialInfo.getRootUrl();
        String rootFolderInAlfresco = AlfrescoConstant.ROOT_URL;

        String year = AlfrescoConstant.PREFIX_YEAR + getCurrentFiscalYear();
        String month = getCurrentMonth();
        //String tenderId = AlfrescoConstant.PREFIX_TENDER + land.getId();

        /*return new StringBuilder(rootFolderInAlfresco).append("/")
            .append(land.getNameOfTheLand()).append("/").append(year)
            .append("/").append(month).append("/").append(tenderId)
            .toString();   */
        //TODO: append the module id to url
        return new StringBuilder(rootFolderInAlfresco).append("/").append(landId).toString();
    }

    /**
     * Gets the company home.
     *
     * @return the company home
     */
    private static ParentReference getCompanyHome() {
        ParentReference companyHomeParent = new ParentReference(
                AlfrescoConstant.STORE, null, "/app:company_home",
                Constants.ASSOC_CONTAINS, null);
        return companyHomeParent;
    }

    /**
     * Creates the space.
     *
     * @param parentref the parentref
     * @param spaceName the space name
     * @return the reference
     * @throws Exception the exception
     */
    private static Reference createSpace(Reference parentref, String spaceName)
            throws Exception {

        if (logger.isDebugEnabled()) {
            logger.debug("Starting Creating space: " + spaceName);
        }

        Reference space = null;
        logger.debug("space assigned null");
        ParentReference parent = getReferenceToParent(parentref);

        logger.debug("Starting Creating space 1 : " + spaceName);

        try {

            logger.debug("Starting Creating space 2 : " + spaceName);

            space = new Reference(AlfrescoConstant.STORE, null,
                    parent.getPath() + "/cm:" + normilizeNodeName(spaceName));

            logger.debug("Starting Creating space 3 : " + spaceName);

            WebServiceFactory.getRepositoryService().get(new Predicate(new Reference[]{space},
                    AlfrescoConstant.STORE, null));

            logger.debug("Starting Creating space 4 : " + spaceName);

            if (logger.isDebugEnabled()) {
                logger.debug("The space named " + spaceName
                        + " already exists. Hence skip creation.");
            }

        } catch (Exception e1) {
            e1.printStackTrace();
            if (logger.isDebugEnabled()) {
                logger.debug("The space named " + spaceName
                        + " does not exist. Creating it.");
            }

            // Set the space's property name
            parent.setChildName(Constants.createQNameString(
                    Constants.NAMESPACE_CONTENT_MODEL,
                    normilizeNodeName(spaceName)));

            NamedValue[] properties = new NamedValue[]{Utils
                    .createNamedValue(Constants.PROP_NAME, spaceName)};

            // Create the space using CML (Content Manipulation Language)
            CMLCreate create = new CMLCreate(AlfrescoConstant.DUMMY_ID, parent,
                    null, null, null, Constants.TYPE_FOLDER, properties);
            CML cml = new CML();
            cml.setCreate(new CMLCreate[]{create});

            // Execute the CML create statement
            try {
                WebServiceFactory.getRepositoryService().update(cml);
            } catch (Exception e2) {
                logger.error("Can not create the space:" + e2);
                throw new BusinessException("tender.docupload.failed");
            }
        }

        return space;

    }

    /**
     * Convert to lucene path.
     *
     * @param uploadURL the upload url
     * @return the string
     */
    private static String convertToLucenePath(String uploadURL) {
        String url;

        url = uploadURL.trim().replaceAll(" ", "_");

        // add a slash at the beginning if not already present
        if (url.indexOf("/") != 0) {
            url = "/" + url;
        }

        // remove the slash from end if present
        if (url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }

        String lucenePath = AlfrescoConstant.COMPANY_HOME_LUCENE_PATH
                + url.replaceAll("/", "/cm:");

        if (logger.isDebugEnabled()) {
            logger.debug("Final lucenePath: " + lucenePath);
        }
        return lucenePath;
    }

    /**
     * Find item.
     *
     * @param path     the path
     * @param fileName the file name
     * @return the result set row
     * @throws BusinessException the business exception
     */
    private static ResultSetRow findItem(String path, String fileName)
            throws BusinessException {
        Query query = new Query(Constants.QUERY_LANG_LUCENE, "+PATH:\"" + path
                + "/*\" +@cm\\:name:\"" + fileName + "\"");
        ResultSet resultSet = getResultSetRow(query,
                AlfrescoConstant.SERVER_URL, AlfrescoConstant.USER_NAME,
                AlfrescoConstant.PASSWORD);

        // hopefully this query will return only one row
        if (resultSet.getRows() != null && resultSet.getRows().length > 0) {
            return resultSet.getRows()[0];
        } else {
            return null;
        }
    }

    /**
     * Gets the result set row.
     *
     * @param query  the query
     * @param AlfUrl the alf url
     * @param usrId  the usr id
     * @param pwd    the pwd
     * @return the result set row
     * @throws BusinessException the business exception
     */
    private static ResultSet getResultSetRow(Query query, String AlfUrl,
                                             String usrId, String pwd) throws BusinessException {
        try {
            QueryResult queryResult = WebServiceFactory.getRepositoryService()
                    .query(AlfrescoConstant.STORE, query, false);
            ResultSet resultSet = queryResult.getResultSet();
            return resultSet;
        } catch (Exception ex) {
            logger.error("search failed:" + ex);
            throw new BusinessException("tender.docupload.failed");
        }
    }

    /**
     * Normilize node name.
     *
     * @param name the name
     * @return the string
     */
    private static String normilizeNodeName(String name) {
        return name.trim().replace(" ", "_");
    }

    /**
     * Gets the reference to parent.
     *
     * @param spaceref the spaceref
     * @return the reference to parent
     */
    private static ParentReference getReferenceToParent(Reference spaceref) {
        ParentReference parent = new ParentReference();
        try {
            parent.setStore(AlfrescoConstant.STORE);
            parent.setPath(spaceref.getPath());
            parent.setUuid(spaceref.getUuid());
            parent.setAssociationType(Constants.ASSOC_CONTAINS);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return parent;
    }

    /**
     * Return current fiscal year.
     *
     * @return year
     */
    public static int getCurrentFiscalYear() {
        Calendar cal = Calendar.getInstance();
        int currentYear = cal.get(Calendar.YEAR);
        /*
           * if(cal.get(Calendar.MONTH)>Calendar.MARCH){ currentYear++; }
           */
        return currentYear;
    }

    /**
     * This method returns current month.
     *
     * @return the current month
     */
    public static String getCurrentMonth() {
        Calendar cal = Calendar.getInstance();
        int monthId = cal.get(Calendar.MONTH);
        DateFormatSymbols dfs = new DateFormatSymbols();
        String[] months = dfs.getMonths();
        String month = "";
        if (monthId >= 0 && monthId <= 11) {
            month = months[monthId];
        }
        return month;
    }

    /**
     * Return Mime Type File.
     *
     * @param fileName the file name
     * @return the mime type
     */

    public static String getMimeType(String fileName) {
        String mimeType = null;
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        mimeType = fileNameMap.getContentTypeFor(fileName);

        //if (CommonUtil.checkBlank(mimeType)) {
        mimeType = new MimetypesFileTypeMap().getContentType(fileName);
        // }
        return mimeType;
    }

}
